package de.koller.aframe.util.math.interpolation;

import de.koller.aframe.util.math.FunctionPreview;
import de.koller.aframe.util.math.IFunction;

public class AInterpolation {
	/** x=0 at v1, x=1 at v2 */
	public static float interpolate( float v0, float v1, float v2, float v3, float x ) {
		float p = (v3 - v2) - (v0 - v1);
		float q = (v0 - v1) - p;
		float r = v2 - v0;
		float s = v1;

		return p*x*x*x + q*x*x + r*x + s;
	}
	
	public static float interpolateDerivate( float v0, float v1, float v2, float v3, float x ) {
		float p = (v3 - v2) - (v0 - v1);
		float q = (v0 - v1) - p;
		float r = v2 - v0;
		float s = v1;

		return 3*p*x*x + 2*q*x + r;
	}
	
	public static IFunction create( final InterpolateBuilder builder ) {
		return new InterpolatedFunction( builder );
	}
	
	public static IFunction createDerivate( final InterpolateBuilder builder ) {
		return new InterpolatedDerivitedFunction( builder );
	}
	
	private static class InterpolatedFunction implements IFunction {
		private final InterpolateBuilder builder;

		private InterpolatedFunction( InterpolateBuilder builder ) {
			this.builder = builder;
		}
		
		@Override
		public float getValue( float x ) {
			float ix = (x - builder.xCoordOfv1) / builder.spaceBetweenPoints;
			return AInterpolation.interpolate( builder.v0, builder.v1, builder.v2, builder.v3, ix );
		}
		
	}
	
	private static class InterpolatedDerivitedFunction implements IFunction {
		private final InterpolateBuilder builder;

		private InterpolatedDerivitedFunction( InterpolateBuilder builder ) {
			this.builder = builder;
		}
		
		@Override
		public float getValue( float x ) {
			float ix = (x - builder.xCoordOfv1) / builder.spaceBetweenPoints;
			return AInterpolation.interpolateDerivate( builder.v0, builder.v1, builder.v2, builder.v3, ix );
		}
	}
	
	public static class InterpolateBuilder {
		public float spaceBetweenPoints = 1;
		public float xCoordOfv1 = 0;
		
		public float v0, v1, v2, v3;

		public InterpolateBuilder( float v0, float v1, float v2, float v3 ) {
			this.v0 = v0;
			this.v1 = v1;
			this.v2 = v2;
			this.v3 = v3;
		}
		
		public InterpolateBuilder( float xCoordOfv1, float spaceBetweenPoints, float v0, float v1, float v2, float v3 ) {
			this( v0, v1, v2, v3 );
			this.xCoordOfv1 = xCoordOfv1;
			this.spaceBetweenPoints = spaceBetweenPoints;
		}
	}
	
	public static void main( String[] args ) {
//		FunctionPreview.dontscale( -1 );
//		FunctionPreview.showFunction( -1, 3,
//				AInterpolation.create( new InterpolateBuilder( 1, 1,  .5f,  .5f,  .5f,  .5f ) ),
//				AInterpolation.create( new InterpolateBuilder( 1, 1,  .5f,  .5f, -.5f, -.5f ) ),
//				AInterpolation.create( new InterpolateBuilder( 1, 1, -.5f, -.5f,  .5f,  .5f ) ),
//				AInterpolation.create( new InterpolateBuilder( 1, 1, -.5f, -.5f, -.5f, -.5f ) ) );
		
		FunctionPreview.dontscale( -1f );
		FunctionPreview.showFunction( -.5f, 1.5f, 
				AInterpolation.create( new InterpolateBuilder( 1, 0, 0, 1 ) ),
				AInterpolation.createDerivate( new InterpolateBuilder( 1, 0, 0, 1 ) ) );
	}

}
